package com.njworkorder.Controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.njworkorder.DTO.PlanAddDto;
import com.njworkorder.DTO.PlanMonthlyAllocationAddDto;
import com.njworkorder.Entity.*;
import com.njworkorder.Mapper.PlanMeteringMapper;
import com.njworkorder.Service.AssignmentService;
import com.njworkorder.Service.MeteringInventoryService;
import com.njworkorder.Service.PlanMonthlyAllocationService;
import com.njworkorder.Service.PlanService;
import com.njworkorder.Utils.TreeBuilder;
import com.njworkorder.Utils.Result;
import com.njworkorder.Utils.ResultUtil;
import com.njworkorder.VO.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Year;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Tag(name = "计划管理",description = "计划管理相关接口")
@RestController
@RequestMapping("/plan")
public class PlanController {

    private final PlanService planService;
    private final PlanMeteringMapper meteringMapper;
    private final AssignmentService assignmentService;
    private final PlanMonthlyAllocationService planMonthlyAllocationService;
    private final MeteringInventoryService meteringInventoryService;
    public PlanController(PlanService planService,
                          PlanMeteringMapper meteringMapper,AssignmentService assignmentService,
                          PlanMonthlyAllocationService planMonthlyAllocationService,
                          MeteringInventoryService meteringInventoryService) {
        this.planService = planService;
        this.meteringMapper = meteringMapper;
        this.assignmentService = assignmentService;
        this.planMonthlyAllocationService = planMonthlyAllocationService;
        this.meteringInventoryService = meteringInventoryService;
    }

    @Transactional
    @Operation(summary = "添加计划")
    @PostMapping("/addInfo")
    public Result<String> addInfo(@RequestBody PlanAddDto planAddDto){

        Plan plan = new Plan();
        String id = UUID.randomUUID().toString().replace("-","");
        plan.setId(id);
        BeanUtils.copyProperties(planAddDto,plan);

        meteringAnalysis(id,planAddDto.getPlanMeteringInfos());

        boolean save = planService.save(plan);
        if (save){
            return ResultUtil.success("添加成功");
        }
        return ResultUtil.fail("添加失败");
    }

    @Transactional
    @Operation(summary = "删除计划")
    @GetMapping("/deleteById")
    public Result<String> deleteById(@RequestParam String id){
        boolean removeById = planService.removeById(id);

        LambdaQueryWrapper<PlanMetering> queryWrapper = new LambdaQueryWrapper<PlanMetering>()
                .eq(PlanMetering::getPlanId,id);
        meteringMapper.delete(queryWrapper);

        if (removeById){
            return ResultUtil.success("删除成功");
        }
        return ResultUtil.fail("删除失败");
    }

    @Transactional
    @Operation(summary = "修改计划")
    @PostMapping("/updateById/{id}")
    public Result<String> updateById(@RequestBody PlanAddDto planAddDto,
                                     @PathVariable("id") String id){
        Plan plan = new Plan();
        plan.setId(id);
        BeanUtils.copyProperties(planAddDto,plan);
        boolean updateById = planService.updateById(plan);
        if (updateById){
            LambdaQueryWrapper<PlanMetering> queryWrapper = new LambdaQueryWrapper<PlanMetering>()
                    .eq(PlanMetering::getPlanId,id);
            meteringMapper.delete(queryWrapper);

            meteringAnalysis(id,planAddDto.getPlanMeteringInfos());

            return ResultUtil.success("修改成功");
        }
        return ResultUtil.fail("修改失败");
    }


    @Operation(summary = "查询计划详情")
    @GetMapping("/getDetailsById")
    public Result<PlanDetailsVo> getDetailsById(@RequestParam("id") String id){
        MPJLambdaWrapper<Plan> wrapper = new MPJLambdaWrapper<Plan>()
                .selectAll(Plan.class)
                .leftJoin(AdministrativeOrganization.class,AdministrativeOrganization::getId,Plan::getAoId,ext ->
                        ext.selectAs(AdministrativeOrganization::getName, PlanDetailsVo::getAoName))
                .leftJoin(Section.class,Section::getId,Plan::getSectionId,ext ->
                        ext.selectAs(Section::getSectionName, PlanDetailsVo::getSectionName))
                .leftJoin(Workspace.class,Workspace::getId,Plan::getWorkspaceId,ext ->
                        ext.selectAs(Workspace::getWorkspaceName, PlanDetailsVo::getWorkspaceName))
                .eq(Plan::getId,id);
        PlanDetailsVo planDetailsVo = planService.selectJoinOne(PlanDetailsVo.class, wrapper);

        LambdaQueryWrapper<PlanMetering> queryWrapper = new LambdaQueryWrapper<PlanMetering>()
                .eq(PlanMetering::getPlanId,id);
        List<PlanMetering> planMeteringList = meteringMapper.selectList(queryWrapper);
        List<String> list = planMeteringList.stream().map(PlanMetering::getMeteringInventoryId).toList();

        planDetailsVo.setMeteringIds(list);

        return ResultUtil.success(planDetailsVo);
    }


    @Operation(summary = "计划列表")
    @GetMapping("/getList")
    public Result<Page<MeteringSortVO>> getList(@RequestParam("pageIndex") int pageIndex,
                                                @RequestParam("pageSize") int pageSize,
                                                @Schema(description = "计价类型(1基本总价、2单价计量)") @RequestParam(value = "pricingType", required = false) String pricingType,
                                                @Schema(description = "项目号") @RequestParam(value = "projectNo", required = false) String projectNo,
                                                @Schema(description = "路段id") @RequestParam(value = "sectionId", required = false) String sectionId,
                                                @Schema(description = "工区id") @RequestParam(value = "workspaceId", required = false) String workspaceId,
                                                @Schema(description = "合同开始日期") @RequestParam(value = "contractStartTime", required = false) String contractStartTime,
                                                @Schema(description = "合同结束日期") @RequestParam(value = "contractEndTime", required = false) String contractEndTime,
                                                @Schema(description = "维修机构") @RequestParam(value = "aoId", required = false) String aoId) {

        MPJLambdaWrapper<Plan> wrapper = new MPJLambdaWrapper<Plan>()
                .selectAs(Plan::getPlanName,MeteringSortVO::getPlanName)
                .selectAs(Plan::getId, MeteringSortVO::getId)
                .selectAs(Plan::getMeteringYear, MeteringSortVO::getProjectNo)
                .selectAs(Plan::getStatus,MeteringSortVO::getUnit)
                .selectAs(Plan::getPublishTime,MeteringSortVO::getPublishTime)
                .selectAs(Plan::getContractStartTime, MeteringSortVO::getContractStartTime)
                .selectAs(Plan::getContractEndTime, MeteringSortVO::getContractEndTime)
                .selectAs(Plan::getContractNumber, MeteringSortVO::getContractNumber)

                .leftJoin(AdministrativeOrganization.class,AdministrativeOrganization::getId,Plan::getAoId,ext ->
                        ext.selectAs(AdministrativeOrganization::getName, MeteringSortVO::getUnitPrice))
                .leftJoin(Section.class,Section::getId,Plan::getSectionId,ext ->
                        ext.selectAs(Section::getSectionName, MeteringSortVO::getProjectName))
                .leftJoin(Workspace.class,Workspace::getId,Plan::getWorkspaceId,ext ->
                        ext.selectAs(Workspace::getWorkspaceName, MeteringSortVO::getWorkspaceName))

                .apply("1=1")
                .eq(StringUtils.isNotEmpty(sectionId),Plan::getSectionId,sectionId)
                .eq(StringUtils.isNotEmpty(workspaceId),Plan::getWorkspaceId,workspaceId)
                .ge(StringUtils.isNotEmpty(contractStartTime),Plan::getContractStartTime,contractStartTime)
                .le(StringUtils.isNotEmpty(contractEndTime),Plan::getContractEndTime,contractEndTime)
                .eq(StringUtils.isNotEmpty(aoId),Plan::getAoId,aoId);
        Page<MeteringSortVO> planListVoIPage = planService.selectJoinListPage(new Page<>(pageIndex,pageSize), MeteringSortVO.class, wrapper);


//        List<String> planIds = planListVoIPage.getRecords().stream().map(MeteringSortVO::getId).toList();
//        MPJLambdaWrapper<PlanMetering> wrapper1 = new MPJLambdaWrapper<PlanMetering>()
//                .select(PlanMetering::getPlanId,PlanMetering::getUnitPrice)
//                .selectAs(PlanMetering::getId,MeteringSortVO::getPlanMeteringId)
//                .selectAs(PlanMetering::getPricingType,MeteringSortVO::getPricingType)
//                .select(MeteringInventory::getId,
//                        MeteringInventory::getProjectNo,
//                        MeteringInventory::getProjectName,
//                        MeteringInventory::getUnit,
//                        MeteringInventory::getParentId
//                        )
//                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
//                .apply("1=1")
//                .eq(StringUtils.isNotEmpty(pricingType), PlanMetering::getPricingType,pricingType)
//                .in(!planIds.isEmpty(),PlanMetering::getPlanId,planIds)
//                .eq(StringUtils.isNotEmpty(projectNo),MeteringInventory::getProjectNo,projectNo);
//        List<MeteringSortVO> meteringList = meteringMapper.selectJoinList(MeteringSortVO.class, wrapper1);
//
//        for (MeteringSortVO meteringSortVO : planListVoIPage.getRecords()) {
//            List<MeteringSortVO> list = meteringList.stream().filter(f -> f.getPlanId().equals(meteringSortVO.getId())).toList();
//            meteringSortVO.setMaintenanceOrganization(meteringSortVO.getUnitPrice());
//            meteringSortVO.setSubset(TreeBuilder.buildTree(list, "subset"));
//        }

        return ResultUtil.success(planListVoIPage);
    }

    @Operation(summary = "计划列表(京沪使用)")
    @GetMapping("/getPlanList")
    public Result<List<MeteringSortVO>> getList1(@RequestParam(value = "pageIndex",required = false) String pageIndex,
                                                @RequestParam(value = "pageSize",required = false) String pageSize) {

        MPJLambdaWrapper<Plan> wrapper = new MPJLambdaWrapper<Plan>()
                .selectAs(Plan::getPlanName,MeteringSortVO::getPlanName)
                .selectAs(Plan::getId, MeteringSortVO::getId)
                .selectAs(Plan::getMeteringYear, MeteringSortVO::getProjectNo)
                .selectAs(Plan::getStatus,MeteringSortVO::getUnit)
                .selectAs(Plan::getPublishTime,MeteringSortVO::getPublishTime)
                .selectAs(Plan::getContractStartTime, MeteringSortVO::getContractStartTime)
                .selectAs(Plan::getContractEndTime, MeteringSortVO::getContractEndTime)
                .selectAs(Plan::getContractNumber, MeteringSortVO::getContractNumber)

                .leftJoin(AdministrativeOrganization.class,AdministrativeOrganization::getId,Plan::getAoId,ext ->
                        ext.selectAs(AdministrativeOrganization::getName, MeteringSortVO::getUnitPrice))
                .leftJoin(Section.class,Section::getId,Plan::getSectionId,ext ->
                        ext.selectAs(Section::getSectionName, MeteringSortVO::getProjectName))
                .leftJoin(Workspace.class,Workspace::getId,Plan::getWorkspaceId,ext ->
                        ext.selectAs(Workspace::getWorkspaceName, MeteringSortVO::getWorkspaceName))

                .apply("1=1");

        if(StringUtils.isNotEmpty(pageIndex) && StringUtils.isNotEmpty(pageSize)){
            Page<MeteringSortVO> planListVoIPage = planService.selectJoinListPage(new Page<>(Integer.parseInt(pageIndex),Integer.parseInt(pageSize)), MeteringSortVO.class, wrapper);
            return ResultUtil.success(planListVoIPage.getRecords());
        }
        List<MeteringSortVO> meteringSortVOS = planService.selectJoinList(MeteringSortVO.class, wrapper);

        return ResultUtil.success(meteringSortVOS);
    }


    @Operation(summary = "通过计划id获取清单数据")
    @GetMapping("/getPlanMById")
    public Result<List<MeteringSortVO>> getPlanMById(@RequestParam("id") String id,
                                                     @Schema(description = "计价类型(1基本总价、2单价计量)") @RequestParam(value = "pricingType", required = false) String pricingType,
                                                     @Schema(description = "项目号") @RequestParam(value = "projectNo", required = false) String projectNo){

        MPJLambdaWrapper<PlanMetering> wrapper1 = new MPJLambdaWrapper<PlanMetering>()
                .select(PlanMetering::getPlanId,PlanMetering::getUnitPrice)
                .selectAs(PlanMetering::getId,MeteringSortVO::getPlanMeteringId)
                .selectAs(PlanMetering::getPricingType,MeteringSortVO::getPricingType)
                .selectAs(PlanMetering::getAmount,MeteringSortVO::getAmountM)
                .select(MeteringInventory::getId,
                        MeteringInventory::getProjectNo,
                        MeteringInventory::getProjectName,
                        MeteringInventory::getUnit,
                        MeteringInventory::getParentId
                )
                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .eq(PlanMetering::getPlanId,id)
                .eq(StringUtils.isNotEmpty(pricingType), PlanMetering::getPricingType,pricingType)
                .eq(StringUtils.isNotEmpty(projectNo),MeteringInventory::getProjectNo,projectNo)
                .orderByAsc(MeteringInventory::getProjectNo);


        List<MeteringSortVO> meteringList = meteringMapper.selectJoinList(MeteringSortVO.class, wrapper1);

        List<MeteringSortVO> subset = TreeBuilder.buildTree(meteringList, "subset");

        for (MeteringSortVO meteringSortVO : subset) {
            BigDecimal bd1 = BigDecimal.valueOf(calculateSubsetSumStream(meteringSortVO));
            BigDecimal result1 = bd1.setScale(2, RoundingMode.HALF_UP);
            meteringSortVO.setTotalAmount(result1.doubleValue());
        }



        return ResultUtil.success(subset);

    }

    public static double calculateSubsetSumStream(MeteringSortVO user) {
        if (user == null || user.getSubset() == null || user.getSubset().isEmpty()) {
            return 0.0;
        }

        return user.getSubset().parallelStream()
                .mapToDouble(u -> {
                    double currentSum = u.getAmountM() * Double.parseDouble(u.getUnitPrice());
                    if (u.getSubset() != null && !u.getSubset().isEmpty()) {
                        currentSum += calculateSubsetSumStream(u);
                    }
                    return currentSum;
                })
                .sum();
    }



    @Operation(summary = "根据计划id查询关联清单层级")
    @GetMapping("/getPlanMeteringByPlanId")
    public  Result<List<MeteringSortVO>> getPlanMeteringByPlanId(@RequestParam("planId") String planId) {

        MPJLambdaWrapper<PlanMetering> wrapper1 = new MPJLambdaWrapper<PlanMetering>()
                .select(PlanMetering::getPlanId,PlanMetering::getUnitPrice)
                .selectAs(PlanMetering::getId,MeteringSortVO::getPlanMeteringId)
                .select(MeteringInventory::getId,
                        MeteringInventory::getProjectNo,
                        MeteringInventory::getProjectName,
                        MeteringInventory::getUnit,
                        MeteringInventory::getParentId
                )
                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .eq(PlanMetering::getPlanId,planId);
        List<MeteringSortVO> meteringList = meteringMapper.selectJoinList(MeteringSortVO.class, wrapper1);

        List<MeteringSortVO> subset = TreeBuilder.buildTree(meteringList, "subset");

        return ResultUtil.success(subset);
    }



//    private Page<MeteringSortVO> getOtherList(int pageIndex,int pageSize,
//                                              String pricingType,String planType,String projectNo,
//                                              String sectionId,String workspaceId,String contractStartTime,String contractEndTime,String aoId){
//
//        MPJLambdaWrapper<PlanMetering> wrapper1 = new MPJLambdaWrapper<PlanMetering>()
//                .select(PlanMetering::getPlanId,PlanMetering::getUnitPrice)
//                .selectAs(PlanMetering::getId,MeteringSortVO::getPlanMeteringId)
//                .select(MeteringInventory::getId,
//                        MeteringInventory::getProjectNo,
//                        MeteringInventory::getProjectName,
//                        MeteringInventory::getUnit,
//                        MeteringInventory::getParentId
//                )
//                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
//                .leftJoin(Plan.class,Plan::getId,PlanMetering::getPlanId)
//                .eq(StringUtils.isNotEmpty(planType),Plan::getPlanType,planType)
//                .eq(MeteringInventory::getProjectNo,projectNo);
//        Page<MeteringSortVO> meteringSortVOPage = meteringMapper.selectJoinPage(new Page<>(pageIndex, pageSize), MeteringSortVO.class, wrapper1);
//
//        List<String> PlanIds = meteringSortVOPage.getRecords().stream().map(MeteringSortVO::getPlanId).toList();
//
//
//
//        MPJLambdaWrapper<Plan> wrapper = new MPJLambdaWrapper<Plan>()
//                .selectAs(Plan::getId, MeteringSortVO::getId)
//                .selectAs(Plan::getMeteringYear, MeteringSortVO::getProjectNo)
//                .selectAs(Plan::getStatus,MeteringSortVO::getUnit)
//                .selectAs(Plan::getPublishTime,MeteringSortVO::getPublishTime)
//                .selectAs(Plan::getContractStartTime, MeteringSortVO::getContractStartTime)
//                .selectAs(Plan::getContractEndTime, MeteringSortVO::getContractEndTime)
//                .selectAs(Plan::getPlanType,MeteringSortVO::getPlanType)
//
//                .leftJoin(AdministrativeOrganization.class,AdministrativeOrganization::getId,Plan::getAoId,ext ->
//                        ext.selectAs(AdministrativeOrganization::getName, MeteringSortVO::getUnitPrice))
//                .leftJoin(Section.class,Section::getId,Plan::getSectionId,ext ->
//                        ext.selectAs(Section::getSectionName, MeteringSortVO::getProjectName))
//                .leftJoin(Workspace.class,Workspace::getId,Plan::getWorkspaceId,ext ->
//                        ext.selectAs(Workspace::getWorkspaceName, MeteringSortVO::getWorkspaceName))
//
//                .apply("1=1")
//                .eq(StringUtils.isNotEmpty(planType),Plan::getPlanType,planType)
//                .eq(StringUtils.isNotEmpty(pricingType),Plan::getPricingType,pricingType)
//                //.eq(StringUtils.isNotEmpty(projectNo),Plan::getMeteringYear,projectNo)
//                .eq(StringUtils.isNotEmpty(sectionId),Plan::getSectionId,sectionId)
//                .eq(StringUtils.isNotEmpty(workspaceId),Plan::getWorkspaceId,workspaceId)
//
//                .ge(StringUtils.isNotEmpty(contractStartTime),Plan::getContractStartTime,contractStartTime)
//                .le(StringUtils.isNotEmpty(contractEndTime),Plan::getContractEndTime,contractEndTime)
//
//                .eq(StringUtils.isNotEmpty(aoId),Plan::getAoId,aoId)
//                .in(Plan::getId,PlanIds);
//        //List<MeteringSortVO> meteringSortVOS = planService.selectJoinList(MeteringSortVO.class, wrapper);
//
//        Page<MeteringSortVO> planListVoIPage = planService.selectJoinListPage(new Page<>(pageIndex,pageSize), MeteringSortVO.class, wrapper);
//
//        for (MeteringSortVO meteringSortVO : planListVoIPage.getRecords()) {
//            List<MeteringSortVO> list = meteringSortVOPage.getRecords().stream().filter(f -> f.getPlanId().equals(meteringSortVO.getId())).toList();
//            meteringSortVO.setMaintenanceOrganization(meteringSortVO.getUnitPrice());
//            meteringSortVO.setSubset(list);
//        }
//
//        planListVoIPage.setTotal(meteringSortVOPage.getTotal());
//        planListVoIPage.setPages(meteringSortVOPage.getPages());
//        planListVoIPage.setCurrent(meteringSortVOPage.getCurrent());
//        planListVoIPage.setSize(meteringSortVOPage.getSize());
//
//        return planListVoIPage;
//    }

    @Operation(summary = "计划跟踪")
    @PostMapping("/planTrack")
    public Result<Page<MeteringSortVO>> planTrack(@RequestParam("pageIndex") int pageIndex,
                                                  @RequestParam("pageSize") int pageSize,
                                                  @Schema(description = "计价类型(1基本总价、2单价计量)") @RequestParam(value = "pricingType", required = false) String pricingType,
                                                  @Schema(description = "项目名称") @RequestParam(value = "projectName",required = false) String projectName,
                                                  @Schema(description = "路段名称") @RequestParam(value = "sectionName",required = false) String sectionName,
                                                  @Schema(description = "计划开始时间") @RequestParam(value = "startTime",required = false) String startTime,
                                                  @Schema(description = "计划结束时间") @RequestParam(value = "endTime",required = false) String endTime) {

        MPJLambdaWrapper<Plan> wrapper = new MPJLambdaWrapper<Plan>()
                .selectAs(Plan::getId, MeteringSortVO::getId)
                .selectAs(Plan::getMeteringYear, MeteringSortVO::getProjectNo)
                .selectAs(Plan::getStatus,MeteringSortVO::getUnit)
                .selectAs(Plan::getPublishTime,MeteringSortVO::getPublishTime)
                .selectAs(Plan::getContractStartTime, MeteringSortVO::getContractStartTime)
                .selectAs(Plan::getContractEndTime, MeteringSortVO::getContractEndTime)
                .selectAs(Plan::getPlanName, MeteringSortVO::getPlanName)

                .leftJoin(AdministrativeOrganization.class,AdministrativeOrganization::getId,Plan::getAoId,ext ->
                        ext.selectAs(AdministrativeOrganization::getName, MeteringSortVO::getUnitPrice))
                .leftJoin(Section.class,Section::getId,Plan::getSectionId,ext ->
                        ext.selectAs(Section::getSectionName, MeteringSortVO::getProjectName))
                .leftJoin(Workspace.class,Workspace::getId,Plan::getWorkspaceId,ext ->
                        ext.selectAs(Workspace::getWorkspaceName, MeteringSortVO::getWorkspaceName))
                .like(StringUtils.isNotEmpty(sectionName),Section::getSectionName,sectionName)

                .between(StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime),Plan::getContractStartTime,startTime,endTime);
        Page<MeteringSortVO> planListVoIPage = planService.selectJoinListPage(new Page<>(pageIndex,pageSize), MeteringSortVO.class, wrapper);

        List<String> planIds = planListVoIPage.getRecords().stream().map(MeteringSortVO::getId).toList();
        MPJLambdaWrapper<PlanMetering> wrapper1 = new MPJLambdaWrapper<PlanMetering>()
                .select(PlanMetering::getPlanId,PlanMetering::getUnitPrice,PlanMetering::getUnit,PlanMetering::getAmount)
                .select("IFNULL((SELECT SUM(nums) FROM bill_of_quantities AS boq WHERE t.id = boq.mc_id), 0) AS nums")
                .select(MeteringInventory::getId,
                        MeteringInventory::getProjectNo,
                        MeteringInventory::getProjectName,
                        //MeteringInventory::getUnitPrice,
                        //MeteringInventory::getUnit,
                        MeteringInventory::getParentId
                        //MeteringInventory::getAmount
                )

                .selectAs("COALESCE(t2.num, 0)",MeteringSortVO::getUseNum)

                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .leftJoin(MeteringUse.class,on ->
                        on.eq(MeteringUse::getProjectCode,MeteringInventory::getProjectNo).eq(MeteringUse::getPricingType, pricingType))
                .eq(StringUtils.isNotEmpty(pricingType), PlanMetering::getPricingType,pricingType)
                .in(!planIds.isEmpty(),PlanMetering::getPlanId,planIds)
                .like(StringUtils.isNotEmpty(projectName),MeteringInventory::getProjectName,projectName)
                .orderByAsc(MeteringInventory::getProjectNo);
        List<MeteringSortVO> meteringList = meteringMapper.selectJoinList(MeteringSortVO.class, wrapper1);

        for (MeteringSortVO meteringSortVO : planListVoIPage.getRecords()) {
            List<MeteringSortVO> list = meteringList.stream().filter(f -> f.getPlanId().equals(meteringSortVO.getId())).toList();
            meteringSortVO.setSubset(TreeBuilder.buildTree(list, "subset"));
        }

        return ResultUtil.success(planListVoIPage);
    }

    @Operation(summary = "查询清单是否超出计划预算")
    @GetMapping("/planExceed")
    public Result<Map<String,Boolean>> planExceed(@RequestParam("time") String time){

        Result<PlanMeteringSortVo> summaryTable = getSummaryTable();
        double totalPrice = summaryTable.getData().getTotalPrice();


        LambdaQueryWrapper<Plan> wapper = new LambdaQueryWrapper<>();
        wapper.orderByDesc(Plan::getPublishTime).last("limit 1");
        Plan one = planService.getOne(wapper);


        MPJLambdaWrapper<PlanMetering> wrapper = new  MPJLambdaWrapper<PlanMetering>()
                .select("sum(t.unit_price * t2.num) as specialUnitPrice")
                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .leftJoin(MeteringUse.class,MeteringUse::getProjectCode,MeteringInventory::getProjectNo)
                .eq(PlanMetering::getPlanId,one.getId()).eq(PlanMetering::getPricingType,1).eq(MeteringUse::getPricingType,1)
                .le(StringUtils.isNotEmpty(time),MeteringUse::getCreateTime,time);
        PlanMetering planMetering = meteringMapper.selectJoinOne(wrapper);

        Map<String,Boolean> map = new HashMap<>();
        if (planMetering == null){
            map.put("exceed",false);
        }else {
            map.put("exceed",planMetering.getSpecialUnitPrice().doubleValue() > (totalPrice / 2));
        }


        return ResultUtil.success(map);
    }


    @Operation(summary = "养护资金进度展示")
    @GetMapping("/getMaintenanceFundsProgress")
    public Result<List<MaintenanceFundsProgressVo>> getMaintenanceFundsProgress(@RequestParam(value = "season",required = false) String season,
                                                                                @RequestParam(value = "year",required = false) String year) throws ExecutionException, InterruptedException {
        final List<String> monthsForSeason;
        if(StringUtils.isNotEmpty(season)){
            monthsForSeason = getMonthsForSeason(season, String.valueOf(year));
        }else {
            int quarter = getQuarter(new Date());
            monthsForSeason = getMonthsForSeason(String.valueOf(quarter),String.valueOf(year));
        }

        CompletableFuture<List<MaintenanceFundsProgressVo>> maintenanceFundsProgressVos = CompletableFuture.supplyAsync(() ->{
            MPJLambdaWrapper<Plan> wrapper = new MPJLambdaWrapper<Plan>()
                    .selectAs("sum(unit_price * is_special)", MaintenanceFundsProgressVo::getPlannedExpenses)
                    .selectAs("DATE_FORMAT(contract_start_time,'%Y-%m')",MaintenanceFundsProgressVo::getDate)
                    .leftJoin(PlanMetering.class,PlanMetering::getPlanId,Plan::getId)

                    .isNotNull(PlanMetering::getUnitPrice).isNotNull(PlanMetering::getIsSpecial)
                    .func(i ->
                            i.between(Plan::getContractStartTime,monthsForSeason.getFirst(),monthsForSeason.getLast())
                    )
                    .groupBy(MaintenanceFundsProgressVo::getDate);
            return planService.selectJoinList(MaintenanceFundsProgressVo.class, wrapper);
        });


        CompletableFuture<List<MaintenanceFundsProgressVo>> maintenanceFundsProgressVos1 = CompletableFuture.supplyAsync(() ->{
            MPJLambdaWrapper<Assignment> wrapper1 = new MPJLambdaWrapper<Assignment>()
                    .selectAs("sum(nums * unit_price)", MaintenanceFundsProgressVo::getActualCost)
                    .selectAs("DATE_FORMAT(final_judgment_time,'%Y-%m')",MaintenanceFundsProgressVo::getDate)

                    .leftJoin(BillOfQuantities.class,BillOfQuantities::getJobManagementId,Assignment::getId)

                    .eq(Assignment::getAuditStatus,7)
                    .func(i -> i.between(Assignment::getFinalJudgmentTime,monthsForSeason.getFirst(),monthsForSeason.getLast()))
                    .groupBy(MaintenanceFundsProgressVo::getDate);
            return assignmentService.selectJoinList(MaintenanceFundsProgressVo.class, wrapper1);
        });


        CompletableFuture.allOf(maintenanceFundsProgressVos,maintenanceFundsProgressVos1).get();


        List<MaintenanceFundsProgressVo> maintenanceFundsProgressVos4 = new ArrayList<>();
        MaintenanceFundsProgressVo maintenanceFundsProgressVo;
        for (String item : monthsForSeason){
            maintenanceFundsProgressVo = new MaintenanceFundsProgressVo();
            String substring = item.substring(0, item.length() - 3);
            List<MaintenanceFundsProgressVo> list = maintenanceFundsProgressVos.get().stream().filter(f -> f.getDate().equals(substring)).toList();
            List<MaintenanceFundsProgressVo> list1 = maintenanceFundsProgressVos1.get().stream().filter(f -> f.getDate().equals(substring)).toList();
            if(list.isEmpty() && list1.isEmpty()){
                maintenanceFundsProgressVo.setDate(substring);
            }else{
                maintenanceFundsProgressVo.setDate(substring);
                if(!list.isEmpty()){
                    maintenanceFundsProgressVo.setPlannedExpenses(list.getFirst().getPlannedExpenses());
                }
                if(!list1.isEmpty()){
                    maintenanceFundsProgressVo.setActualCost(list1.getFirst().getActualCost());
                }
            }
            maintenanceFundsProgressVos4.add(maintenanceFundsProgressVo);
        }

        return ResultUtil.success(maintenanceFundsProgressVos4);
    }


    @Operation(summary = "汇总表清单")
    @GetMapping("/getSummaryTable")
    public Result<PlanMeteringSortVo> getSummaryTable(){

        PlanMeteringSortVo planMeteringSortVo = new PlanMeteringSortVo();

        List<PMSummaryTable> pmSummaryTable = planService.getPMSummaryTable();
        Map<String, List<PMSummaryTable>> collect = pmSummaryTable.stream().collect(Collectors.groupingBy(PMSummaryTable::getPricingType));
        collect.forEach((key, value) -> {
            if(key.equals("1")){
                planMeteringSortVo.setBasicTotalPrice(value.stream().sorted(Comparator.comparingInt(item -> Integer.parseInt(item.getProjectNo()))).toList());
            }else {
                planMeteringSortVo.setUnitPriceMeasurement(value.stream().sorted(Comparator.comparingInt(item -> Integer.parseInt(item.getProjectNo()))).toList());
            }
        });

        planMeteringSortVo.setAmountTo(pmSummaryTable.stream().mapToDouble(PMSummaryTable::getSumCount).sum());
        planMeteringSortVo.setSafetyProductionExpenses(Math.round((planMeteringSortVo.getAmountTo() * 0.02) * 100) / 100.0);
        planMeteringSortVo.setTotalPrice(planMeteringSortVo.getAmountTo() + planMeteringSortVo.getSafetyProductionExpenses() + planMeteringSortVo.getIndependentFee());

        return ResultUtil.success(planMeteringSortVo);
    }


    @Operation(summary = "根据计划id获取计划月度分配初始数据")
    @GetMapping("/getPlanMonthlyAllocationInitData")
    public Result<List<Map<String,Object>>> getPlanMonthlyAllocationInitData(@RequestParam("planId") String planId){


        MPJLambdaWrapper<PlanMetering> wrapper1 = new MPJLambdaWrapper<PlanMetering>()
                .select(PlanMetering::getPlanId,PlanMetering::getUnitPrice,PlanMetering::getAmount,PlanMetering::getPricingType)
                .select("IFNULL((SELECT SUM(nums) FROM bill_of_quantities AS boq WHERE t.id = boq.mc_id), 0) AS nums")
                .select(MeteringInventory::getId,
                        MeteringInventory::getProjectNo,
                        MeteringInventory::getProjectName,
                        MeteringInventory::getUnit,
                        MeteringInventory::getParentId
                )
                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .eq(PlanMetering::getPlanId,planId);
        List<MeteringSortVO> meteringList = meteringMapper.selectJoinList(MeteringSortVO.class, wrapper1);


        List<MeteringSortVO> subset = TreeBuilder.buildTree(meteringList, "subset");

        List<Map<String,Object>> list = new ArrayList<>();
        Map<String,Object> map;
        for(MeteringSortVO item : subset){
            map = new HashMap<>();
            map.put("projectName",item.getProjectName());

            List<MeteringSortVO> result = new ArrayList<>();
            searchRecursively(item, result);
            double sum = result.stream().mapToDouble(m -> m.getAmount().doubleValue() * Double.parseDouble(m.getUnitPrice())).sum();
            map.put("initTotalValue",sum);

            // 创建 BigDecimal 对象
            BigDecimal bd = new BigDecimal(sum);
            // 除以 10000
            BigDecimal bigDecimal = bd.divide(new BigDecimal(10000));
            map.put("totalValue",bigDecimal.setScale(2, RoundingMode.HALF_UP));

            map.put("pricingType",item.getPricingType() == null ? "" : item.getPricingType().equals("1") ? "总价" : "单价");

            list.add(map);
        }

        return ResultUtil.success(list);
    }


    @Operation(summary = "添加计划月度分配信息")
    @PostMapping("/addPlanMonthlyAllocation")
    public Result<String> addPlanMonthlyAllocation(@RequestBody List<PlanMonthlyAllocationAddDto> PMAList){

        List<PlanMonthlyAllocation> list = new ArrayList<>();
        PlanMonthlyAllocation planMonthlyAllocation;
        for(PlanMonthlyAllocationAddDto item : PMAList){
            planMonthlyAllocation = new PlanMonthlyAllocation();
            BeanUtils.copyProperties(item,planMonthlyAllocation);
            list.add(planMonthlyAllocation);
        }
        return planMonthlyAllocationService.saveBatch(list) ? ResultUtil.success("添加完成") : ResultUtil.fail("添加失败！");
    }


    @Operation(summary = "根据id更新计划月度分配信息")
    @PostMapping("/updatePMAById/{id}")
    public Result<String> updatePMAById(@PathVariable("id") String id,
                                        @RequestBody PlanMonthlyAllocationAddDto planMonthlyAllocationAddDto){
        PlanMonthlyAllocation planMonthlyAllocation = new PlanMonthlyAllocation();
        BeanUtils.copyProperties(planMonthlyAllocationAddDto,planMonthlyAllocation);
        planMonthlyAllocation.setId(id);
        return planMonthlyAllocationService.updateById(planMonthlyAllocation) ? ResultUtil.success("更新成功") : ResultUtil.fail("更新失败");
    }


    @Operation(summary = "根据id删除计划月度分配信息")
    @GetMapping("/deletePMAById")
    public Result<String> updatePMAById(@RequestParam("id") String id){
        return planMonthlyAllocationService.removeById(id) ? ResultUtil.success("删除成功") : ResultUtil.fail("删除失败");
    }


    @Operation(summary = "根据计划id获取计划月度分配列表")
    @GetMapping("/getPMAListByPlanId")
    public Result<List<PlanMonthlyAllocation>> getPMAListByPlanId(@RequestParam("planId") String planId){

        LambdaQueryWrapper<PlanMonthlyAllocation> wrapper = new LambdaQueryWrapper<PlanMonthlyAllocation>()
                .eq(PlanMonthlyAllocation::getPlanId,planId);

        List<PlanMonthlyAllocation> list = planMonthlyAllocationService.list(wrapper);
        return ResultUtil.success(list);
    }







    private static void searchRecursively(MeteringSortVO item, List<MeteringSortVO> result) {
        // 检查当前节点是否符合条件
        if (item.getAmount().doubleValue() > 0 && Double.parseDouble(item.getUnitPrice()) > 0 ) {
            result.add(item);
        }

        // 如果有子节点，继续递归搜索
        if (item.getSubset() != null && !item.getSubset().isEmpty()) {
            for (MeteringSortVO child : item.getSubset()) {
                searchRecursively(child, result);
            }
        }
    }

    private static List<MeteringSortVO> getP(List<MeteringSortVO> list){
        List<MeteringSortVO> list1 = list.stream().filter(f -> f.getParentId() == null || f.getParentId().isEmpty()).toList();
        List<MeteringSortVO> listRes = new ArrayList<>();
        MeteringSortVO menuLevelVo;
        for (MeteringSortVO meteringSortVO : list1) {
            menuLevelVo = new MeteringSortVO();
            BeanUtils.copyProperties(meteringSortVO,menuLevelVo);
            menuLevelVo.setParentId(meteringSortVO.getPlanId());
            menuLevelVo.setSubset(getSubset(meteringSortVO.getId(),list));
            listRes.add(menuLevelVo);
        }
        return listRes;
    }

    public static List<MeteringSortVO> getSubset(String id,List<MeteringSortVO> list) {
        List<MeteringSortVO> list1 = list.stream().filter(menu -> menu.getParentId().equals(id)).toList();
        List<MeteringSortVO> listRes = new ArrayList<>();
        if (!list1.isEmpty()) {
            MeteringSortVO menuLevelVo;
            for (MeteringSortVO menu : list1) {
                menuLevelVo = new MeteringSortVO();
                BeanUtils.copyProperties(menu,menuLevelVo);
                menuLevelVo.setSubset(getSubset(menu.getId(),list));
                listRes.add(menuLevelVo);
            }
        }
        return listRes;
    }



    private void meteringAnalysis(String planId,List<PlanMeteringInfosVo> planMeteringInfosVos){
        LambdaQueryWrapper<MeteringInventory> wrapper = new LambdaQueryWrapper<MeteringInventory>()
                .select(MeteringInventory::getId,
                        MeteringInventory::getProjectNo,
                        MeteringInventory::getProjectName,
                        MeteringInventory::getPricingType,
                        MeteringInventory::getParentId);
        List<MeteringInventory> list = meteringInventoryService.list(wrapper);

        List<PlanMetering> meteringList = new ArrayList<>();
        PlanMetering planMetering;

        List<MeteringInventory> parentList = new ArrayList<>();

        for (PlanMeteringInfosVo item : planMeteringInfosVos) {
            String pricingType = item.getPricingType().contains("总价") ? "1" : "2";
            List<MeteringInventory> list1 = list.stream().filter(f -> f.getProjectNo().equals(item.getProjectNo()) && f.getPricingType().equals(pricingType)).toList();
            if(!list1.isEmpty()) {
                MeteringInventory first = list1.getFirst();
                List<MeteringInventory> hierarchy = findHierarchy(list, first.getId());
                if(!hierarchy.isEmpty()){
                    int i = 0;
                    for (MeteringInventory item1 : hierarchy) {
                        if(i == 0)
                        {
                            planMetering = new PlanMetering();
                            planMetering.setPlanId(planId);
                            planMetering.setMeteringInventoryId(item1.getId());
                            planMetering.setPricingType(pricingType);
                            planMetering.setUnitPrice(item.getUnitPrice());
                            planMetering.setAmount(item.getAmount());
                            planMetering.setUnit(item.getUnit());
                            planMetering.setIsSpecial(item.getIsSpecial().equals("是") ? "1" : "2");
                            planMetering.setSpecialUnitPrice(item.getSpecialUnitPrice());
                            meteringList.add(planMetering);
                        }else {
                            parentList.add(item1);
                        }
                        i++;
                    }
                }
            }
        }
        List<MeteringInventory> list1 = parentList.stream().distinct().toList();
        for (MeteringInventory item : list1) {
            planMetering = new PlanMetering();
            planMetering.setPlanId(planId);
            planMetering.setMeteringInventoryId(item.getId());
            planMetering.setPricingType(item.getPricingType());
            meteringList.add(planMetering);
        }

        meteringMapper.insert(meteringList);
    }



    private void saveMetering(String id,List<PlanMeteringInfosVo> planMeteringInfosVos){
//        if(planMeteringInfosVos.isEmpty()) return;
//
//        List<String> ids = new ArrayList<>();
//
//        List<PlanMetering> meteringList = new ArrayList<>();
//        PlanMetering planMetering;
//        for (PlanMeteringInfosVo item : planMeteringInfosVos) {
//            String[] split = item.getMeteringInventoryId().split("#");
//
//            ids.addAll(Arrays.asList(split).subList(0, split.length - 1));
//
//            planMetering = new PlanMetering();
//            planMetering.setPlanId(id);
//            planMetering.setMeteringInventoryId(split[split.length-1]);
//            planMetering.setUnitPrice(item.getUnitPrice());
//            planMetering.setAmount(item.getAmount());
//            planMetering.setIsSpecial(item.getIsSpecial());
//            planMetering.setSpecialUnitPrice(item.getSpecialUnitPrice());
//
//            meteringList.add(planMetering);
//        }
//
//        List<String> distinctIds = ids.stream().distinct().toList();
//
//        for (String item : distinctIds) {
//            planMetering = new PlanMetering();
//            planMetering.setPlanId(id);
//            planMetering.setMeteringInventoryId(item);
//            meteringList.add(planMetering);
//        }
//        meteringMapper.insert(meteringList);


//        if(planMeteringInfosVos.isEmpty()) return;
//
//        List<String> ids = new ArrayList<>();
//
//        List<PlanMetering> meteringList = new ArrayList<>();
//        PlanMetering planMetering;
//        for (PlanMeteringInfosVo item : planMeteringInfosVos) {
//            String[] split = item.getMeteringInventoryId().split("#");
//
//            ids.addAll(Arrays.asList(split).subList(0, split.length - 1));
//
//            planMetering = new PlanMetering();
//            planMetering.setPlanId(id);
//            planMetering.setMeteringInventoryId(split[split.length-1]);
//            planMetering.setUnitPrice(item.getUnitPrice());
//            planMetering.setAmount(item.getAmount());
//            planMetering.setIsSpecial(item.getIsSpecial());
//            planMetering.setSpecialUnitPrice(item.getSpecialUnitPrice());
//
//            meteringList.add(planMetering);
//        }
//
//        List<String> distinctIds = ids.stream().distinct().toList();
//
//        for (String item : distinctIds) {
//            planMetering = new PlanMetering();
//            planMetering.setPlanId(id);
//            planMetering.setMeteringInventoryId(item);
//            meteringList.add(planMetering);
//        }
//        meteringMapper.insert(meteringList);


    }



    public static List<MeteringInventory> findHierarchy(List<MeteringInventory> list, String id) {
        // 创建ID到用户的映射
        Map<String, MeteringInventory> userMap = list.stream()
                .collect(Collectors.toMap(MeteringInventory::getId, Function.identity()));

        List<MeteringInventory> hierarchy = new ArrayList<>();
        String currentId = id;
        while (currentId != null && !currentId.isEmpty()) {
            Optional<MeteringInventory> userOpt = Optional.ofNullable(userMap.get(currentId));
            if (userOpt.isEmpty()) {
                break;
            }
            MeteringInventory currentUser = userOpt.get();
            hierarchy.add(currentUser);
            currentId = currentUser.getParentId();
        }
        return hierarchy;
    }




    public List<String> getMonthsForSeason(String season,String year) {
        int currentYear = StringUtils.isNotEmpty(year) ? Integer.parseInt(year) : Year.now().getValue();
        return switch (season) {
            case "1" -> Arrays.asList(currentYear + "-01-01", currentYear + "-02-01", currentYear + "-03-01");
            case "2" -> Arrays.asList(currentYear + "-04-01", currentYear + "-05-01", currentYear + "-06-01");
            case "3" -> Arrays.asList(currentYear + "-07-01", currentYear + "-08-01", currentYear + "-09-01");
            case "4" -> Arrays.asList(currentYear + "-10-01", currentYear + "-11-01", currentYear + "-12-01");
            default -> throw new IllegalArgumentException("Invalid season: " + season);
        };
    }

    public int getQuarter(Date date) {
        // 获取 Calendar 实例
        Calendar calendar = Calendar.getInstance();
        // 设置 Calendar 的时间
        calendar.setTime(date);
        // 获取月份（注意：Calendar 的月份是从 0 开始的，所以需要加 1）
        int month = calendar.get(Calendar.MONTH) + 1;
        // 根据月份确定季度
        if (month <= 3) {
            return 1; // 第一季度
        } else if (month <= 6) {
            return 2; // 第二季度
        } else if (month <= 9) {
            return 3; // 第三季度
        } else {
            return 4; // 第四季度
        }
    }


    @Operation(summary = "根据年份和工区id获取计划关联清单信息")
    @GetMapping("/getPlanInfo")
    public Result<List<Map<String,String>>> getPlanInfo(@RequestParam("workareId") String workareId,
                              @RequestParam("year") int year) {

        LambdaQueryWrapper<Plan> wrapper = new LambdaQueryWrapper<Plan>()
                .eq(Plan::getMeteringYear, year).eq(StringUtils.isNotEmpty(workareId),Plan::getWorkspaceId, workareId)
                .orderByDesc(Plan::getPublishTime).last("limit 1");
        Plan one = planService.getOne(wrapper);

        if (one == null) {
            return ResultUtil.fail("没有符合条件的数据!");
        }

        MPJLambdaWrapper<PlanMetering> wrapper1 = new MPJLambdaWrapper<PlanMetering>()
                .select(PlanMetering::getUnitPrice,PlanMetering::getUnit)
                .select(MeteringInventory::getId,
                        MeteringInventory::getProjectNo,
                        MeteringInventory::getProjectName,
                        MeteringInventory::getParentId
                )
                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .in(PlanMetering::getPlanId,one.getId());
        List<MeteringSortVO> meteringList = meteringMapper.selectJoinList(MeteringSortVO.class, wrapper1);

        if (meteringList.isEmpty()) {
            return ResultUtil.success(new ArrayList<>());
        }

        List<MeteringSortVO> bottomLevelEmployees = getBottomLevelEmployees(meteringList);

        List<Map<String,String>> mapList = new ArrayList<>();
        Map<String,String> map;
        for (MeteringSortVO item : bottomLevelEmployees) {
            map = new HashMap<>();
            map.put("projectCode",item.getProjectNo());
            map.put("projectName",item.getProjectName());
            map.put("unit",item.getUnit());
            map.put("unitPrice", String.valueOf(item.getUnitPrice()));
            mapList.add(map);
        }
        return ResultUtil.success(mapList);
    }

    public List<MeteringSortVO> getBottomLevelEmployees(List<MeteringSortVO> lists) {
        // 收集所有作为父级的ID
        Set<String> parentIds = lists.stream()
                .map(MeteringSortVO::getParentId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        // 找出那些ID不在parentIds集合中的用户，即最底层员工
        return lists.stream()
                .filter(f -> !parentIds.contains(f.getId()))
                .collect(Collectors.toList());
    }

}
